﻿namespace HZY.Framework.Repository.EntityFramework.Attributes;

/// <summary>
/// DbContext 配置注解
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class DbContextConfigAttribute : Attribute
{
    /// <summary>
    /// 命名规则默认 蛇形 命名 SysFunction => sys_function
    /// </summary>
    public NameRuleType NameRuleType { get; set; } = NameRuleType.SnakeCase;

    /// <summary>
    /// 实体命名空间 可以是正则表达式匹配的命名空间
    /// </summary>
    public string EntityNamespace { get; set; }

    /// <summary>
    /// DbContext 配置注解
    /// </summary>
    /// <param name="entityNamespace"></param>
    public DbContextConfigAttribute(string entityNamespace)
    {
        EntityNamespace = entityNamespace;
    }

    /// <summary>
    /// DbContext 配置注解
    /// </summary>
    /// <param name="entityNamespace"></param>
    /// <param name="nameRuleType"></param>
    public DbContextConfigAttribute(string entityNamespace, NameRuleType nameRuleType)
    {
        EntityNamespace = entityNamespace;
        NameRuleType = nameRuleType;
    }

    /// <summary>
    /// 获取模型类型集合
    /// </summary>
    /// <param name="dbContextType"></param>
    /// <param name="predicate"></param>
    /// <returns></returns>
    public List<Type> GetModelTypes(Type dbContextType, Func<Type, bool>? predicate = null)
    {
        var assembly = dbContextType.Assembly;
        var types = (from w in assembly.ExportedTypes.ToList()
                    where w.IsClass && w.IsPublic && !w.IsGenericType && !w.IsAbstract
                    where w.GetInterface(nameof(IEntity)) != null || typeof(IEntity).IsAssignableFrom(w)
                    //where !w.Name.StartsWith(nameof(DefaultEntity)) && !w.Name.Contains(nameof(FullEntity))
                    select w)
                .WhereIf(predicate != null, predicate!)
                .Where(w => Regex.IsMatch(w.FullName!, EntityNamespace))
                .ToList()
            ;

        //扫描类型下面的 dbset model
        var propertyInfoList =
            dbContextType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        var dbSets = propertyInfoList.Where(w => w.PropertyType.Name == "DbSet`1");

        foreach (var dbSet in dbSets)
        {
            if (dbSet.PropertyType.GenericTypeArguments.Length <= 0) continue;

            var model = dbSet.PropertyType.GenericTypeArguments[0];

            if (types.Any(w => w.FullName == model.FullName)) continue;

            types.Add(model);
        }

        return types;
    }

    /// <summary>
    /// 模型创建
    /// </summary>
    /// <param name="modelBuilder"></param>
    /// <param name="modelTypes"></param>
    /// <returns></returns>
    public List<Type> OnModelCreating(ModelBuilder modelBuilder, List<Type> modelTypes)
    {
        #region 自动注册 dbset

        var types = modelTypes;

        foreach (var type in types)
        {
            if (modelBuilder.Model.FindEntityType(type) != null)
            {
                continue;
            }

            modelBuilder.Model.AddEntityType(type);
        }

        #endregion

        #region 软删除

        // 开始命中删除字段
        var deleteEntityAllList = modelBuilder.Model
                .GetEntityTypes()
                .Where(w => typeof(IEntity).IsAssignableFrom(w.ClrType))
            ;
        foreach (var item in deleteEntityAllList)
        {
            var entityType = item.ClrType;

            // 如果有逻辑删除的标记那么跳过不处理
            if (TableLogicAttribute.HasTableLogic(entityType))
            {
                HandleTableLogicByTableLogicAttribute(entityType, modelBuilder);
                continue;
            }

            if (typeof(IBaseDeleteEntity).IsAssignableFrom(entityType))
            {
                HandleTableLogicByIBaseDeleteEntity(entityType, modelBuilder);
                continue;
            }

            if (typeof(IBaseDeleteEntityV2).IsAssignableFrom(entityType))
            {
                HandleTableLogicByIBaseDeleteEntityV2(entityType, modelBuilder);
                continue;
            }
        }

        #endregion

        #region 自动映射表名

        NamingConvention(modelBuilder, NameRuleType);

        #endregion

        return types;
    }

    /// <summary>
    /// 处理带有特性标记 TableLogic 的逻辑删除
    /// </summary>
    /// <param name="entityType"></param>
    /// <param name="modelBuilder"></param>
    private void HandleTableLogicByTableLogicAttribute(Type entityType, ModelBuilder modelBuilder)
    {
        var logicAttribute = TableLogicAttribute.GetTableLogic(entityType);
        if (logicAttribute is null)
        {
            return;
        }

        var propertyInfos = entityType.GetProperties();
        foreach (var propertyInfo in propertyInfos)
        {
            var tableLogicAttribute = TableLogicAttribute.GetTableLogicAttribute(propertyInfo);
            var logicExpression = tableLogicAttribute?.GetLogicExpression(entityType, propertyInfo);
            if (logicExpression is null)
            {
                continue;
            }

            modelBuilder.Entity(entityType).HasQueryFilter(logicExpression);
        }
    }

    /// <summary>
    /// 实体类继承了 IBaseDeleteEntity 的逻辑删除
    /// </summary>
    /// <param name="entityType"></param>
    /// <param name="modelBuilder"></param>
    private void HandleTableLogicByIBaseDeleteEntity(Type entityType, ModelBuilder modelBuilder)
    {
        #region 过滤软删除 条件是：实体必须继承自 IBaseDeleteEntity

        var delName = nameof(IBaseDeleteEntity.IsDeleted);
        var propertyInfo = entityType.GetProperty(delName);
        if (propertyInfo is null)
        {
            return;
        }

        var lambdaExpression = EntityFrameworkUtil.GetFilter(entityType, $" !w.{delName} ");

        // var lambda = ExpressionTreeExtensions.NotEqual(item.ClrType,delName, true, typeof(bool));
        modelBuilder.Entity(entityType).HasQueryFilter(lambdaExpression);

        #endregion
    }

    /// <summary>
    /// 实体类继承了 IBaseDeleteEntity 的逻辑删除
    /// </summary>
    /// <param name="entityType"></param>
    /// <param name="modelBuilder"></param>
    private void HandleTableLogicByIBaseDeleteEntityV2(Type entityType, ModelBuilder modelBuilder)
    {
        #region 过滤软删除 条件是：实体必须继承自 IBaseDeleteEntity

        if (!typeof(IBaseDeleteEntity).IsAssignableFrom(entityType))
        {
            return;
        }

        var delName = nameof(IBaseDeleteEntityV2.DeleteFlag);
        var propertyInfo = entityType.GetProperty(delName);
        if (propertyInfo is null)
        {
            return;
        }

        var lambdaExpression =
            EntityFrameworkUtil.GetFilter(entityType, $" w.{delName} == false || w.{delName} == null");

        modelBuilder.Entity(entityType).HasQueryFilter(lambdaExpression);

        #endregion
    }

    /// <summary>
    /// 命名规则转化
    /// </summary>
    private static void NamingConvention(ModelBuilder modelBuilder, NameRuleType nameRuleType)
    {
        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            NamingConventionToTableName(entity, nameRuleType);
            NamingConventionToFieldName(entity, nameRuleType);
        }
    }

    /// <summary>
    /// 表名称命名规则
    /// </summary>
    /// <param name="mutableEntityType"></param>
    /// <param name="nameRuleType"></param>
    private static void NamingConventionToTableName(IMutableEntityType mutableEntityType, NameRuleType nameRuleType)
    {
        var type = mutableEntityType.ClrType;

        var tableAttribute = type.GetCustomAttribute<TableAttribute>();
        if (tableAttribute != null)
        {
            return;
        }

        var entityDescriptionAttribute = type.GetCustomAttribute<TableNameAttribute>();
        if (entityDescriptionAttribute?.NameRuleMode == NameRuleMode.FieldName)
        {
            return;
        }

        var ruleType = entityDescriptionAttribute?.NameRuleType ?? nameRuleType;
        var oldName = mutableEntityType.GetTableName() ?? type.Name;
        mutableEntityType.SetTableName(EntityFrameworkUtil.GetNameByNameRuleType(oldName, ruleType));

        //mutableEntityType.SetTableName(tableAttribute.Name);
    }

    /// <summary>
    /// 字段名称命名规则
    /// </summary>
    /// <param name="mutableEntityType"></param>
    /// <param name="nameRuleType"></param>
    private static void NamingConventionToFieldName(IMutableEntityType mutableEntityType, NameRuleType nameRuleType)
    {
        var type = mutableEntityType.ClrType;

        var entityDescriptionAttribute = type.GetCustomAttribute<TableNameAttribute>();
        if (entityDescriptionAttribute?.NameRuleMode == NameRuleMode.TableName)
        {
            return;
        }

        var mutableProperties = mutableEntityType.GetProperties();

        foreach (var item in mutableProperties)
        {
            if (item.PropertyInfo is null)
            {
                continue;
            }

            var columnAttribute = item.PropertyInfo.GetCustomAttribute<ColumnAttribute>();
            if (columnAttribute is not null && !string.IsNullOrWhiteSpace(columnAttribute.Name))
            {
                continue;
            }

            var ruleType = entityDescriptionAttribute?.NameRuleType ?? nameRuleType;
            var oldColumnName = item.GetColumnName();
            var newColumnName = EntityFrameworkUtil.GetNameByNameRuleType(oldColumnName, ruleType);
            item.SetColumnName(newColumnName);
        }
    }
}